package com.jxbeidou.login.service.impl;

import com.jxbeidou.core.cache.CacheService;
import com.jxbeidou.core.constents.CacheConstants;
import com.jxbeidou.core.constents.CacheKeys;
import com.jxbeidou.core.constents.SecurityConstants;
import com.jxbeidou.core.context.JxbeidouContextHolder;
import com.jxbeidou.core.crypto.util.RequestEncryptUtil;
import com.jxbeidou.core.entity.EncryptEntity;
import com.jxbeidou.core.entity.R;
import com.jxbeidou.core.entity.SecurityUserVO;
import com.jxbeidou.core.enums.EncryptMode;
import com.jxbeidou.core.enums.ResultCode;
import com.jxbeidou.core.enums.SecurityResultCode;
import com.jxbeidou.core.exception.ServerException;
import com.jxbeidou.core.util.JwtUtil;
import com.jxbeidou.core.util.UUIDUtil;
import com.jxbeidou.login.entity.JwtVO;
import com.jxbeidou.login.entity.LoginUserDTO;
import com.jxbeidou.login.listener.LoginListenerUtil;
import com.jxbeidou.login.service.LoginUserService;
import com.jxbeidou.system.api.user.UserApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

/**
 * @program: jxbeidou-basic
 * @ClassName: LoginUserServiceImpl
 * @description: 用户登录实现
 * @author: 杜晓祥
 * @create: 2023-12-26 15:53
 * 版权所有：交信北斗(北京)信息科技有些公司
 */
@Service
@Slf4j
public class LoginUserServiceImpl implements LoginUserService {

    @Resource
    private UserApi userApi;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private CacheService cacheService;

    @Resource
    private LoginListenerUtil listenerUtil;


    @Value("${spring.profiles.active}")
    private String profile;

    @Override
    public SecurityUserVO findByUserInfo(String ip, LoginUserDTO user) {
        log.info("[LoginUserServiceImpl.findByUserInfo]用户登录,username:[{}]", user.getUsername());
        R<SecurityUserVO> result = userApi.findByUsername(user.getUsername());
        if (result == null) {
            log.warn("[LoginUserServiceImpl.findByUserInfo]用户查询出现问题");
            listenerUtil.loginFailed(new SecurityUserVO(-1L), ip);
            throw new ServerException(SecurityResultCode.FIND_USER_FAILED);
        }
        if (result.getCode() != ResultCode.SUCCESS.getCode()) {
            log.warn("[LoginUserServiceImpl.findByUserInfo]用户查询出现问题，message:[{}]", result.getMessage());
            listenerUtil.loginFailed(new SecurityUserVO(-1L), ip);
            throw new ServerException(SecurityResultCode.FIND_USER_FAILED);
        }
        SecurityUserVO securityUserVO = result.getData();
        if (securityUserVO == null) {
            log.warn("[LoginUserServiceImpl.findByUserInfo]没有找的用户数据，无法登录,username:[{}]", user.getUsername());
            listenerUtil.loginFailed(new SecurityUserVO(-1L), ip);
            throw new ServerException(SecurityResultCode.USERNAME_NOT_FOUND_FAILED);
        }
        if (!securityUserVO.getStatus()) {
            log.warn("[LoginUserServiceImpl.findByUserInfo]用户被锁定，无法登录,username:[{}]", securityUserVO.getUsername());
            listenerUtil.loginFailed(securityUserVO, ip);
            throw new ServerException(SecurityResultCode.ACCOUNT_DISABLED_FAILED);
        }
        if (passwordEncoder.matches(user.getPassword(), securityUserVO.getPassword())) {
            securityUserVO.setUserKey(UUIDUtil.simpleUUID());
            return securityUserVO;
        }
        listenerUtil.loginFailed(securityUserVO, ip);
        throw new ServerException(SecurityResultCode.PASSWORD_FAILED);
    }

    @Override
    public JwtVO generateToken(String ip, LoginUserDTO user) {
        SecurityUserVO securityUserVO = findByUserInfo(ip, user);
        JwtVO result = new JwtVO();
        try {
            String token = JwtUtil.generateTokenToHs(securityUserVO);
            result.setToken(SecurityConstants.PREFIX + token);
            Duration duration = Duration.ofMinutes(SecurityConstants.JWS_EXP);
            EncryptEntity encrypt = RequestEncryptUtil.generateKey(EncryptMode.RSA);
            if (encrypt != null) {
                result.setEncryptMode(encrypt.getMode().getMode());
                result.setPublicKey(encrypt.getPublicKey());
                log.debug("[LoginUserServiceImpl.generateToken]获取到加密方式：mode:[{}]", encrypt.getMode());
            }
            if ("dev".equals(profile)) {
                log.debug("[LoginUserServiceImpl.generateToken]当前为运行环境为：{},设置登录有效期为12小时");
                duration = Duration.ofHours(12L);
            }
            cacheService.set(CacheConstants.SYS_LOGIN_USER_KEY +
                    securityUserVO.getUserKey() + SecurityConstants.ENCRYPT_SUFFIX, encrypt, duration);
            cacheService.set(CacheConstants.SYS_LOGIN_USER_KEY + securityUserVO.getUserKey(), token, duration);
        } catch (Exception e) {
            log.warn("[LoginUserServiceImpl.generateToken]TOKEN生成出现错误", e);
            listenerUtil.loginFailed(securityUserVO, ip);
            throw new ServerException(SecurityResultCode.ACCOUNT_TOKEN_FAILED);
        }
        LocalDateTime time = LocalDateTime.now();
        result.setStartTime(time);
        result.setExpirationTime(time.plus(SecurityConstants.JWS_EXP, ChronoUnit.MINUTES));
        listenerUtil.loginSuccess(securityUserVO, ip);
        return result;
    }

    @Override
    public boolean logout(String ip) {
        String key = JxbeidouContextHolder.getUserKey();
        if (StringUtils.isEmpty(key)) {
            log.warn("[LoginUserServiceImpl#logout]没有找的登录信息");
            return false;
        }
        String token = cacheService.get(CacheConstants.SYS_LOGIN_USER_KEY + key, String.class);
        if (StringUtils.isEmpty(token)) {
            log.warn("[LoginUserServiceImpl#logout]没有找的登录信息, token:[{}]", token);
            return false;
        }
        try {
            SecurityUserVO user = JwtUtil.verifyTokenByHs(token);
            if (user == null) {
                log.warn("[LoginUserServiceImpl#logout]解析到的用户为空, token:[{}]", token);
                return false;
            }
            cacheService.del(CacheKeys.getAccessTokenKey(token));
            cacheService.del(CacheConstants.SYS_LOGIN_USER_KEY + key);
            cacheService.del(CacheConstants.SYS_LOGIN_USER_KEY + key + SecurityConstants.ENCRYPT_SUFFIX);
            listenerUtil.logoutSuccess(user, ip);
            return true;
        } catch (Exception e) {
            log.warn("[LoginUserServiceImpl#logout]登出失败,message:[{}]", e.getMessage());
        }
        return false;
    }
}
